物件屬性描述器 Property Descriptor(一)


Posted by nosleepengineer on 2023-07-25

前言

初學 JavaScript 時,對於物件的理解很基本,就是一個大刮號里面包含了 key、value 的配對,對於有一些屬性好像無法被列舉(或枚舉?意思是不能被 for-in 等方法撈出來),有一些屬性不能刪除或修改,get 、set 是什麼,有什麼作用完全不了解。

對這些特性缺乏了解導致在閱讀某些 source code 或技術文章的時後無法完全理解發生什麼事,像是 Vue2 怎麼實現雙向綁定,Object.defineProperty 幹了什麼。下面的內容,會解釋什麽是物件屬性描述器、怎麽查看、修改屬性描述器和它們的功能。

聲明物件

首先先聲明一個包含屬性和方法的物件。聲明物件的方法可以透過字面量或 New Object() 的方式,以下使用字面量聲明的方式。

let obj = {
    a: 0,                    // 屬性
    b: [ 1, 2 ],             // 屬性
    c: 'A',                  // 屬性
    d: { x: 'B', y: 'C' },   // 屬性
    e: function(x, y) {
        return x + y;
    }                        // 方法
}

上面注解中的屬性指的是物件中的一般變量,方法指的是函數。

新增屬性

建立物件後,有兩種方式可以新增或編輯屬性

指派運算子 =
obj.f = '0'
Object.defineProperty
Object.defineProperty(obj, 'g', { value: '1' })

Object.defineProperties(obj, {
  h: { value: '2' },
  i: { value: '3' }
})

取得屬性描述

透過以下的靜態方法可以檢視物件中的屬性描述器。

Object.getOwnPropertyDescriptor(obj, prop)

呼叫 Object.getOwnPropertyDescriptor(obj, prop) 來檢視物件中的某個屬性的描述,第一個參數傳入目標物件,第二個參數傳入屬性名稱,就能返回該屬性的描述。

const des = Object.getOwnPropertyDescriptor(obj, 'a')
console.log(des)

Object.getOwnPropertyDescriptors(obj)

上述的方法只能返回一個屬性的描述,如果希望一次返回物件中所有屬性的描述,可以使用 Object.getOwnPropertyDescriptors(obj),並將目標物件作為參數傳入方法。

const desAll = Object.getOwnPropertyDescriptors(obj)
console.log(desAll)


透過上面兩個方法,我們可以看到物件中無論是屬性或是方法,都有它的描述,分別是 configurable、enumerable、value、writable。

屬性描述器也是一個物件,根據內容可以分為:資料描述器( data descriptor )存取器描述器( accessor descriptors ),屬性描述器必定屬於其中一種,不能同為兩者或兩者皆非。上面輸出的屬性描述器皆為資料描述器,資料描述器是指被描述的屬性是有值 ( value ) 的屬性,並且在一般情況下可以修改。存取器描述器則是指那些透過 get 和 set 所描述的屬性。

資料描述器 ( data descriptor )

包含以下四個屬性:

1. configurable

決定屬性的描述器內容是否可修改和屬性本身是否可以被刪除,當值為 false 時,透過 delete 刪除屬性或 Object.defineProperty 修改描述器內容接無效,唯一的例外是 writable 依然可以由 true 改為 false。

Object.defineProperty(obj, 'a', {
    configurable: false
})

// 刪除屬性
delete obj.a // return false

// 修改描述器
Object.defineProperty(obj, 'a', {
    enumerable: false
}) // 返回錯誤

// 修改 writable 屬性
Object.defineProperty(obj, 'a', {
    writable: false
}) // 修改成功
2. enumerable

決定屬性是否可被枚舉。當值為 false 時,for-in、Object.keys(obj)、Object.values(obj)、Object.assign({}, obj)、JSON.stringify(obj)、{...obj} 等方法都無法獲取該屬性,只有透過 Object.getOwnPropertyNames(obj) 可以獲得所有屬性的 key。另外,在 Chrome 的 console 中把 obj 輸出時,值為 false 的屬性 key 值的顏色較淺。

3. value

就是屬性的值,只有在資料描述器中才存在的屬性。

4. writable

決定屬性是否可透過指派運算子 = 賦值,當值為 false 時只能透過 Object.defineProperty 改變屬性的值,只有在資料描述器中才存在的屬性。

透過指派運算子 = 來新增一個物件中的屬性時, writable、enumerable、configurable 預設值為 true,但若以靜態方法 Object.defineProperty() 定義屬性並且只定義屬性的 value 時,writable、enumerable、configurable 預設為 false。

下一篇文章會介紹存取器描述器。


#javascript #object







Related Posts

[FE101] HTML 基礎

[FE101] HTML 基礎

[Note] JS - OOP: Static Members

[Note] JS - OOP: Static Members

【Day05】Youtube GUI 下載程式

【Day05】Youtube GUI 下載程式


Comments